package bma.common.netty.handler;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelDownstreamHandler;
import org.jboss.netty.channel.ChannelEvent;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelUpstreamHandler;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import bma.common.langutil.core.ExceptionUtil;
import bma.common.langutil.log.LogPrinter;
import bma.common.langutil.log.LogPrinter.LEVEL;

public class ChannelHandlerLog implements ChannelUpstreamHandler,
		ChannelDownstreamHandler {

	public enum TYPE {
		ALL, UPSTREAM, DOWNSTREAM
	}

	private LogPrinter log;
	private int traceBufSize = 32;
	private TYPE type = TYPE.ALL;

	public ChannelHandlerLog(LogPrinter log) {
		super();
		this.log = log;
	}

	public ChannelHandlerLog(LogPrinter log, TYPE type) {
		super();
		this.log = log;
		this.type = type;
	}

	public ChannelHandlerLog(LogPrinter log, TYPE type, int traceBufSize) {
		super();
		this.log = log;
		this.type = type;
		if (traceBufSize > 0) {
			this.traceBufSize = traceBufSize;
		}
	}

	public ChannelHandlerLog(String logName, String logType, String type,
			int traceBufSize) {
		this(new LogPrinter(LoggerFactory.getLogger(logName),
				LEVEL.valueOf(logType)), TYPE.valueOf(type), traceBufSize);
	}

	public ChannelHandlerLog(Logger log, LEVEL logLevel, TYPE type,
			int traceBufSize) {
		this(new LogPrinter(log, logLevel), type, traceBufSize);
	}

	public int getTraceBufSize() {
		return traceBufSize;
	}

	public void setTraceBufSize(int traceBufSize) {
		this.traceBufSize = traceBufSize;
	}

	public LogPrinter getLog() {
		return log;
	}

	public void setLog(LogPrinter log) {
		this.log = log;
	}

	public TYPE getType() {
		return type;
	}

	public void setType(TYPE type) {
		this.type = type;
	}

	public void dolog(String flag, ChannelEvent e) {
		Object v = e;
		if (e instanceof ExceptionEvent) {
			ExceptionEvent ee = (ExceptionEvent) e;
			v = ExceptionUtil.toStackTraceString(ee.getCause());
		} else if (e instanceof MessageEvent) {
			MessageEvent me = (MessageEvent) e;
			if (me.getMessage() instanceof ChannelBuffer) {
				ChannelBuffer buf = ((ChannelBuffer) me.getMessage()).copy();
				StringBuffer data = new StringBuffer(this.traceBufSize * 3
						+ this.traceBufSize / 8 + 20);
				data.append("(").append(buf.readableBytes()).append(") ");
				for (int i = 0; i < this.traceBufSize
						&& buf.readableBytes() > 0; i++) {
					if (i != 0) {
						if (i % 8 == 0) {
							data.append("-");
						} else {
							data.append(" ");
						}
					}
					String s = Integer.toHexString(buf.readByte() & 0xFF)
							.toUpperCase();
					if (s.length() < 2)
						data.append("0");
					data.append(s);
				}
				v = data;
			}
		}
		log.print("{} {}", flag, v);
	}

	@Override
	public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e)
			throws Exception {
		if (this.type != TYPE.UPSTREAM) {
			dolog("[D]", e);
		}
		ctx.sendDownstream(e);
	}

	@Override
	public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
			throws Exception {
		if (this.type != TYPE.DOWNSTREAM) {
			dolog("[U]", e);
		}
		ctx.sendUpstream(e);
	}

	public static ChannelPipeline buildPipeline(ChannelPipeline p,
			LogPrinter log) {
		return buildPipeline(p, log, 32);
	}

	public static ChannelPipeline buildPipeline(ChannelPipeline p,
			LogPrinter log, int traceBufSize) {
		p.addFirst("log_up", new ChannelHandlerLog(log,
				ChannelHandlerLog.TYPE.UPSTREAM, traceBufSize));
		p.addLast("log_down", new ChannelHandlerLog(log,
				ChannelHandlerLog.TYPE.DOWNSTREAM, traceBufSize));
		return p;
	}

	@Override
	public String toString() {
		return "CLOG(" + type + ")";
	}
}
